#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
This module is a public domain software, and you may redistribute it and/or modify
it under the same terms as LGPL2.1 license itself, so long as this copyright message
and disclaimer are retained in their original form.

IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

© 2011 Sunny Lin. All Rights Reserved. 
Created on 2011/11/2
'''
__author__ = "Sunny Lin"
__email__ = "013.lin@gmail.com"
__version__ = "0.13"

import os,sys, time, types #,png 
import ConfigParser

class Point:   
  '''A point identified by (x,y) coordinates.
  supports: +, -, *, /, str, repr
  '''
  
  def __init__(self, x=0.0, y=0.0):
    self.x = x
    self.y = y
  
  def __add__(self, p):
    '''Point(x1+x2, y1+y2)'''
    return Point(self.x+p.x, self.y+p.y)
  
  def __sub__(self, p):
    '''Point(x1-x2, y1-y2)'''
    return Point(self.x-p.x, self.y-p.y)
  
  def __mul__( self, scalar ):
    '''Point(x1*x2, y1*y2)'''
    return Point(self.x*scalar, self.y*scalar)
  
  def __div__(self, scalar):
    '''Point(x1/x2, y1/y2)'''
    return Point(self.x/scalar, self.y/scalar)
  
  def __str__(self):
    return "(%s, %s)" % (self.x, self.y)
  
  def __repr__(self):
    return "%s(%r, %r)" % (self.__class__.__name__, self.x, self.y)
  
  def length(self):
    return math.sqrt(self.x**2 + self.y**2)
  
  def distance(self, p):
    '''Calculate the distance between two points.'''
    return (self - p).length()
  
  def middle(self, p):
    '''Calculate the middle point between two points. '''
    return (self + (p-self)/2)
  
  def toTuple(self):
    '''transfer to tuple type (x, y)'''
    return (self.x, self.y)
  
  def clone(self):
    '''Return a full copy of this point.'''
    return Point(self.x, self.y)
  
  def roundPoint(self):
    return Point(round(self.x), round(self.y))
  
  def toInt(self):
    '''Convert co-ordinate values to integers.'''
    self.x = int(self.x)
    self.y = int(self.y)
  
  def toFloat(self):
    '''Convert coordinate values to floats.'''
    self.x = float(self.x)
    self.y = float(self.y)
    
  def slide(self, dx, dy):
    ''' Slide distance dx, dy, means to move to new (x+dx,y+dy). '''
    self.x = self.x + dx
    self.y = self.y + dy
  # End Point

class Rect:

  '''A rectangle identified by two points.
  The rectangle stores left, top, right, and bottom values.
  Coordinates are based on screen coordinates.

  origin                          (left,top)
     +-----> x increases                |------|
     |                                  |      |
     v                                  |------|
  y increases                                 (right,bottom)
  '''

  def __init__(self, pt1, pt2):
    '''Initialize a rectangle from two points.'''
    self.setPoints(pt1, pt2)

  def clone(self):
    '''Return a full copy of this rectangle.'''
    return Rect(self.top_left(), self.bottom_right())

  def setPoints(self, pt1, pt2):
    '''Reset the rectangle coordinates.'''
    if (isinstance(pt1, Point)) :
      (x1, y1) = pt1.toTuple()
    elif (isinstance(pt1, tuple)) :
      (x1, y1) = pt1
    if (isinstance(pt2, Point)) :
      (x2, y2) = pt2.toTuple()
    elif (isinstance(pt2, tuple)) :
      (x2, y2) = pt2
    self.left = min(x1, x2)
    self.top = min(y1, y2)
    self.right = max(x1, x2)
    self.bottom = max(y1, y2)

  def toTuple(self):
    '''(left,top, right, bottom)'''
    return (self.left, self.top, self.right, self.bottom)

  def toTupleWH(self):
    '''(left,top, Width, Height)'''
    return (self.left, self.top, self.width(), self.height())

  def isContains(self, pt_rect):
    '''Return true if a point, or a rectangle is inside the rectangle.'''
    if (isinstance(pt_rect, Point)):
      (x,y) = pt_rect.toTuple()
      return (self.left <= x <= self.right and  self.top <= y <= self.bottom)
    elif (isinstance(pt_rect, Rect)) :
      return (self.left <= pt_rect.left and self.top <= pt_rect.top and
            self.right >= pt_rect.right and self.bottom >= pt_rect.bottom)
    
  def isOverlaps(self, rect):
    '''Return true if a rectangle overlaps this rectangle.'''
    return (self.right > rect.left and self.left < rect.right and
            self.top < rect.bottom and self.bottom > rect.top)

  
  def top_left(self):
    '''Return the top-left corner as a Point.'''
    return Point(self.left, self.top)
  
  def bottom_right(self):
    '''Return the bottom-right corner as a Point.'''
    return Point(self.right, self.bottom)
     
  def width(self):
    '''Return the width of Rectangle. '''
    return (self.right - self.left)

  def height(self):
    '''Return the height of Rectangle. '''
    return (self.bottom - self.top)
  
  def center(self):
    ''' Return the center point of Rectangle '''
    return (self.top_left().middle(self.bottom_right()))
  
  def expand(self, n):
    '''Return a rectangle with extended borders.

    Create a new rectangle that is wider and taller than the
    immediate one. All sides are extended by "n" points.
    '''
    p1 = Point(self.left-n, self.top-n)
    p2 = Point(self.right+n, self.bottom+n)
    return Rect(p1, p2)
    
  def shiftH(self, width):
    ''' Shift the rectangle by horizontal to another position. 
        positive value mean shift to right, and negative value means shift to left '''
    self.left+=width
    self.right+=width
    return Rect(self.top_left(), self.bottom_right())

  def shiftV(self, height):
    ''' Shift the rectangle by vertical to another position. 
        positive value mean shift to down, and negative value means shift to up '''
    self.top+=height
    self.bottom+=height
    return Rect(self.top_left(), self.bottom_right())
    
  def moveTo(self, p):
    ''' Move the rectangle to another top-left point. '''
    width=p.x-self.left
    height=p.y-self.top
    self.shiftH(width)
    self.shiftV(height)
    return Rect(self.top_left(), self.bottom_right())
  
  def __str__( self ):
      return "<Rect (%s,%s)-(%s,%s)>" % (self.left,self.top,
                                         self.right,self.bottom)
  
  def __repr__(self):
      return "%s(%r, %r)" % (self.__class__.__name__,
                             Point(self.left, self.top),
                             Point(self.right, self.bottom))
	

class AreaNameRegistry:
  ''' For those project scope's ActionArea name, need to register here '''
  PHONE="HOME.PHONE"
  LOCK_AND_SOUND="HOME.LOCK_AND_SOUND"
  LOCK="HOME.LOCK"
  SOUND="HOME.SOUND"
  
class ConfigScopeEnum:
  ''' Define the different scope (level) of config (ini file) to load.
        '''
  PROJECT=0
  PACKAGE=1
  CLASS=2
  @classmethod
  def allList(cls):
    return list((0,1,2))

class PositionEnum: 
  '''
    |------------|------------|------------|
    |   LEFTTOP  |  MIDDLETOP |  RIGHTTOP  |
    |            |    TOP     |            |
    |------------|------------|------------|
    | LEFTMIDDLE |MIDDLEMIDDLE| RIGHTMIDDLE|
    |   LEFT     |  CENTER    |    RIGHT   |
    |------------|------------|------------|
    | LEFTBOTTOM |MIDDLEBOTTOM| RIGHTBOTTOM|
    |            |   BOTTOM   |            |
    |------------|------------|------------|
  '''
  LEFTTOP = 11
  LEFT = 12
  LEFTMIDDLE = 12
  LEFTBOTTOM = 13
  TOP = 21
  MIDDLETOP = 21
  CENTER = 22
  MIDDLEMIDDLE = 22
  BOTTOM = 23
  MIDDLEBOTTOM = 23
  RIGHTTOP = 31
  RIGHT = 32
  RIGHTMIDDLE = 32
  RIGHTBOTTOM = 33
# End PositionEnum

class DirectionEnum:
  '''
  LEFT2RIGHT →
  RIGHT2LEFT ←
  TOP2BOTTOM ↓          
  BOTTOM2TOP  ↑
  LEFT_TOP2RIGHT_BOTTOM ↘ 
  RIGHT_BOTTOM2LEFT_TOP ↖  
  RIGHT_TOP2LEFT_BOTTOM ↙
  LEFT_BOTTOM2RIGHT_TOP ↗
  '''
  LEFT2RIGHT = 0
  RIGHT2LEFT = 1
  TOP2BOTTOM = 3
  BOTTOM2TOP = 4
  LEFT_TOP2RIGHT_BOTTOM = 5
  RIGHT_BOTTOM2LEFT_TOP = 6
  RIGHT_TOP2LEFT_BOTTOM = 7
  LEFT_BOTTOM2RIGHT_TOP = 8
# End DirectionEnum

class AreaTypeEnum :
  '''
  '''
  UNDEFINED=0
  BUTTON=1
  TEXT=2
  CHECKBOX=3
  RADIO=4
  ICON=5
  RESULT=99
  
  @classmethod
  def string2Type(cls, string):
    if (string.upper()=='BUTTON') : return AreaTypeEnum.BUTTON
    elif (string.upper()=='TEXT') : return AreaTypeEnum.TEXT
    elif (string.upper()=='CHECKBOX') : return AreaTypeEnum.CHECKBOX
    elif (string.upper()=='RADIO') : return AreaTypeEnum.RADIO
    elif (string.upper()=='ICON') : return AreaTypeEnum.ICON
    elif (string.upper()=='RESULT') : return AreaTypeEnum.RESULT # just an area to test result
    else : return AreaTypeEnum.UNDEFINED
   
class ActionArea(Rect):
  '''
  a class to define a rectangle area in android phone screen which can be used to invoke a kind of function in Android phone.
  And it also devided into 9 small area as below    
      |----|----|----|
      | 11 | 12 | 13 |
      |----|----|----|
      | 12 | 22 | 23 |
      |----|----|----|
      | 13 | 23 | 33 |
      |----|----|----|
  ''' 
  def __init__(self, pt1, pt2, name, areatype=AreaTypeEnum.UNDEFINED):
    ''' provide a name for a ActionArea  '''
    Rect.__init__(self, pt1, pt2)
    self.name=name
    p=self.center()
    self.x=p.x
    self.y=p.y
    self.areaType=areatype
    self._generateArea9()
  
  def _generateArea9(self):
    '''
      |----|----|----|
      | 11 | 12 | 13 |
      |----|----|----|
      | 12 | 22 | 23 |
      |----|----|----|
      | 13 | 23 | 33 |
      |----|----|----|
    '''
    stepx=self.width()/3
    stepy=self.height()/3
    x=self.left
    y=self.top
    self.area9={}
    for xx in range(1,4) :
      for yy in range(1,4) :
        key=("%s%s" % (xx,yy))
        self.area9[key]=Rect((x+stepx*(xx-1), y+stepy*(yy-1)), (x+stepx*xx, y+stepy*yy))
        #print "%s.Area9[%s]:%s" % (self.name, key, self.area9[key])

  def __str__( self ):
      return "<ActionArea-%s (%s,%s)-(%s,%s)>" % (self.name, self.left,self.top, self.right,self.bottom)
  
  def getSubArea(self, position):
    return self.area9[str(position)]
  
  def getDirectionAreas(self, direction):
    ''' According to the DIRECTION, return two areas, those point the direction from the fist one to the second one '''
    if direction==DirectionEnum.TOP2BOTTOM :
      areas=[self.getSubArea(PositionEnum.MIDDLETOP), self.getSubArea(PositionEnum.MIDDLEBOTTOM)]
    elif direction==DirectionEnum.BOTTOM2TOP :
      areas=[self.getSubArea(PositionEnum.MIDDLEBOTTOM), self.getSubArea(PositionEnum.MIDDLETOP)]
    elif direction==DirectionEnum.LEFT2RIGHT :
      areas=[self.getSubArea(PositionEnum.LEFTMIDDLE), self.getSubArea(PositionEnum.RIGHTMIDDLE)]
    elif direction==DirectionEnum.RIGHT2LEFT :
      areas=[self.getSubArea(PositionEnum.RIGHTMIDDLE), self.getSubArea(PositionEnum.LEFTMIDDLE)]
    elif direction==DirectionEnum.LEFT_TOP2RIGHT_BOTTOM :
      areas=[self.getSubArea(PositionEnum.LEFTTOP), self.getSubArea(PositionEnum.RIGHTBOTTOM)]
    elif direction==DirectionEnum.RIGHT_BOTTOM2LEFT_TOP :
      areas=[self.getSubArea(PositionEnum.RIGHTBOTTOM), self.getSubArea(PositionEnum.LEFTTOP)]
    elif direction==DirectionEnum.RIGHT_TOP2LEFT_BOTTOM :
      areas=[self.getSubArea(PositionEnum.RIGHTTOP), self.getSubArea(PositionEnum.LEFTBOTTOM)]
    elif direction==DirectionEnum.LEFT_BOTTOM2RIGHT_TOP :
      areas=[self.getSubArea(PositionEnum.LEFTBOTTOM), self.getSubArea(PositionEnum.RIGHTTOP)]
    return areas

# End ActionArea  
    
class ActionMenu():
  '''
  a class as a container to hold a set of ActionArea 
  '''
  def __init__(self):
    self.action_areas={}
     
  def __str__( self ):
    string=""
    for area in self.action_areas.itervalues() :
      string=string+"<ActionArea[%s] (%s,%s)-(%s,%s)>\n" % (area.name, area.left,area.top, area.right,area.bottom)
    return string

  def addActionArea(self, action_area):
    self.action_areas[action_area.name]=action_area
  
  def getActionArea(self, name):
    if self.action_areas.has_key(name) :
      return self.action_areas[name]
    else : return None
    
  def hasActionArea(self, name):
    return self.action_areas.has_key(name)


def decode_utf8(string):
  return unicode(string,'utf-8').encode('utf-8')

def decode_ascii(string):
  return unicode(string,'utf-8').encode('ascii')

def str2Class(classname):     
  if classname in globals() and isinstance(globals()[classname], types.ClassType):             
    return globals()[classname]     
  else : return None 

  
  
import unittest
class TestActionArea(unittest.TestCase):
    
  def testArea9(self):
    print "%s:Center %s, %s:Center %s, %s:Center %s, %s:Center %s" % (self.rect1, self.rect1.center(), 
                self.rect2, self.rect2.center(), self.rect3, self.rect3.center(), self.rect4, self.rect4.center())
class TestRect(unittest.TestCase):
  def setUp(self):
    self.p1=Point(10,10)
    self.p2=Point(20,20) #
    self.p3=Point(5, 5)
    self.p4=Point(20,5)
    self.p5=Point(5, 20)
    self.rect1=Rect(self.p1, self.p2) 
    self.rect2=Rect(self.p1, self.p3) 
    self.rect3=Rect(self.p1, self.p4) 
    self.rect4=Rect(self.p1, self.p5) 
    print "%s, %s, %s, %s" % (self.rect1, self.rect2, self.rect3, self.rect4)
    
  def testCenter(self):
    print "%s:Center %s, %s:Center %s, %s:Center %s, %s:Center %s" % (self.rect1, self.rect1.center(), 
                self.rect2, self.rect2.center(), self.rect3, self.rect3.center(), self.rect4, self.rect4.center())
    
  def testMoveTo(self):
    p=Point(50,50)
    rect=self.rect1.clone()
    print "%s move to %s = %s" % (self.rect1, p, rect.moveTo(p))
    rect=self.rect2.clone()
    print "%s move to %s = %s" % (self.rect2, p, rect.moveTo(p))
    rect=self.rect3.clone()
    print "%s move to %s = %s" % (self.rect3, p, rect.moveTo(p))
    rect=self.rect4.clone()
    print "%s move to %s = %s" % (self.rect4, p, rect.moveTo(p))

  def testContents(self):
    p=Point(10,10)
    rect=Rect((5,5), (7,7))
    print "%s is contains %s = %s" % (self.rect1, p, self.rect1.isContains(p))
    print "%s is contains %s = %s" % (self.rect1, rect, self.rect1.isContains(rect))
    print "%s is contains %s = %s" % (self.rect2, rect, self.rect2.isContains(rect))
    print "%s is contains %s = %s" % (self.rect3, rect, self.rect3.isContains(rect))
    print "%s is contains %s = %s" % (self.rect4, rect, self.rect4.isContains(rect))

   

if __name__ == '__main__':
    unittest.main()

